package com.caipos.util;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class DateTimeUtil {

    /**
     * 日期时间
     */
    public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";


    /**
     * 日期时间
     */
    public static final String FULL_PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * ISO格式日期时间
     */
    public static final String ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSX";

    /**
     * 完整时间，年月日时分秒
     */
    public static String PATTERN_DATE_FULL_DATE_TIME = "yyyyMMddHHmmss";

    /**
     * 完整时间，年月日
     */
    public static String PATTERN_DATE_FULL_DATE = "yyyyMMdd";

    /**
     * 日期
     */
    public static final String PATTERN_DATE = "yyyy-MM-dd";

    /**
     * 时间
     */
    public static final String PATTERN_TIME = "HH:mm:ss";

    /**
     * 日期时间格式化
     */
    public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATETIME);


    /**
     * ISO格式日期时间格式化
     */
    public static final DateTimeFormatter DATETIME_ISO_FORMATTER = DateTimeFormatter.ofPattern(ISO_8601);

    /**
     * 完整日期格式
     */
    public static final DateTimeFormatter FULL_DATE_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE_FULL_DATE);

    /**
     * 完整日期时间格式
     */
    public static final DateTimeFormatter FULL_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE_FULL_DATE_TIME);

    /**
     * 日期格式化
     */
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE);

    /**
     * 时间格式化
     */
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_TIME);


    /**
     * java8 日期时间格式化
     *
     * @param temporal 时间
     * @return 格式化后的时间
     */
    public static String formatDateTime(TemporalAccessor temporal) {
        return DATETIME_FORMATTER.format(temporal);
    }


    /**
     * 转换为ISO格式日期字符串
     *
     * @param dateTime 时间
     * @return ISO格式日期字符串
     */
    public static String formatISODateTime(LocalDateTime dateTime) {
        return dateTime.atZone(ZoneId.systemDefault()).format(DATETIME_ISO_FORMATTER);
    }

    /**
     * 格式化完整的日期
     *
     * @param date 日期
     * @return 完整的日期
     */
    public static String formatFullDate(LocalDate date) {
        return FULL_DATE_FORMATTER.format(date);
    }

    /**
     * 格式化完整的时间
     *
     * @param dateTime 日期时间
     * @return 完整的时间
     */
    public static String formatFullDateTime(LocalDateTime dateTime) {
        return FULL_DATE_TIME_FORMATTER.format(dateTime);
    }

    /**
     * 转换为日期字符串
     *
     * @param temporal 日期
     * @return 日期字符串
     */
    public static String formatDate(TemporalAccessor temporal) {
        return DATE_FORMATTER.format(temporal);
    }


    /**
     * 转换为时间字符串
     *
     * @param temporal 时间
     * @return 时间字符串
     */
    public static String formatTime(TemporalAccessor temporal) {
        return TIME_FORMATTER.format(temporal);
    }


    /**
     * 日期时间格式化
     *
     * @param temporal 时间
     * @param pattern  格式
     * @return 格式化化后的字符串
     */
    public static String format(TemporalAccessor temporal, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return formatter.format(temporal);
    }

    /**
     * 将字符串转换为 LocalDateTime
     *
     * @param str 字符串
     * @return LocalDateTime对象
     */
    public static LocalDateTime parseDateTime(String str) {
        return LocalDateTime.from(DATETIME_FORMATTER.parse(str));
    }

    /**
     * 将字符串转换为 LocalDate
     *
     * @param str 字符串
     * @return LocalDate对象
     */
    public static LocalDate parseDate(String str) {
        return LocalDate.from(DATE_FORMATTER.parse(str));
    }

    /**
     * 将字符串转换为 LocalTime
     *
     * @param str 字符串
     * @return LocalTime对象
     */
    public static LocalTime parseTime(String str) {
        return LocalTime.from(TIME_FORMATTER.parse(str));
    }

    /**
     * 将ISO格式的日期时间字符串转换为LocalDateTime
     *
     * @param str ISO格式的日期时间字符串
     * @return LocalDateTime
     */
    public static LocalDateTime pareISODateTime(String str) {
        return LocalDateTime.from(DATETIME_ISO_FORMATTER.parse(str));
    }

    /**
     * 将完整日期时间的日期时间字符串转换为LocalDateTime
     *
     * @param str 完整日期的日期字符串
     * @return LocalDateTime
     */
    public static LocalDate parseFullDate(String str) {
        return LocalDate.from(FULL_DATE_FORMATTER.parse(str));
    }

    /**
     * 将完整日期时间的日期时间字符串转换为LocalDateTime
     *
     * @param str 完整日期时间的日期时间字符串
     * @return LocalDateTime
     */
    public static LocalDateTime parseFullDateTime(String str) {
        return LocalDateTime.from(FULL_DATE_TIME_FORMATTER.parse(str));
    }

    /**
     * 将字符串转换为 LocalDateTime
     *
     * @param str     字符串
     * @param pattern 格式
     * @return LocalDateTime对象
     */
    public static LocalDateTime parseDateTime(String str, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.from(formatter.parse(str));
    }

    /**
     * 将字符串转换为 LocalDate
     *
     * @param str     字符串
     * @param pattern 格式
     * @return LocalDate对象
     */
    public static LocalDate parseDate(String str, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.from(formatter.parse(str));
    }

    /**
     * 将字符串转换为 LocalTime
     *
     * @param str     字符串
     * @param pattern 格式
     * @return LocalTime对象
     */
    public static LocalTime parseTime(String str, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalTime.from(formatter.parse(str));
    }

    /**
     * 指定时间的零点时间
     *
     * @param dateTime 日期时间
     * @return 指定时间的零点时间
     */
    public static LocalDateTime toZeroTime(LocalDateTime dateTime) {
        return LocalDateTime.of(dateTime.toLocalDate(), LocalTime.MIN);
    }

    /**
     * 指定时间的24时时间
     *
     * @param dateTime 日期时间
     * @return 指定时间的24时时间
     */
    public static LocalDateTime to24Time(LocalDateTime dateTime) {
        return LocalDateTime.of(dateTime.toLocalDate(), LocalTime.MAX);
    }

    /**
     * localDateTime 转换成毫秒数
     *
     * @param localDateTime LocalDateTime
     * @return long
     */
    public static long toMilliseconds(final LocalDateTime localDateTime) {
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * localDate 转换成毫秒数
     *
     * @param localDate LocalDate
     * @return long
     */
    public static long toMilliseconds(LocalDate localDate) {
        return toMilliseconds(localDate.atStartOfDay());
    }

    /**
     * 转换成java8 时间
     *
     * @param calendar 日历
     * @return LocalDateTime
     */
    public static LocalDateTime fromCalendar(final Calendar calendar) {
        TimeZone tz = calendar.getTimeZone();
        ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
        return LocalDateTime.ofInstant(calendar.toInstant(), zid);
    }

    /**
     * 转换成java8 时间
     *
     * @param instant Instant
     * @return LocalDateTime
     */
    public static LocalDateTime fromInstant(final Instant instant) {
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * 转换成java8 时间
     *
     * @param date Date
     * @return LocalDateTime
     */
    public static LocalDateTime fromDate(final Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * 转换成java8 时间
     *
     * @param milliseconds 毫秒数
     * @return LocalDateTime
     */
    public static LocalDateTime fromMilliseconds(final long milliseconds) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
    }


    /**
     * 无符号正则表达式
     */

    public static final String DATE_FORMAT_NO_SPLIT_REGEX = "^(\\d{4})(\\d{2})(\\d{2})$";

    /**
     * 有符号正常日期格式
     */

    public static final String DATE_FORMAT_COMMON_REGEX = "^(\\d{4,})[/-](\\d{1,2})[/-](\\d{1,2})$";

    /**
     * 有符号正常日期格式替换
     */

    public static final String DATE_FORMAT_COMMON_REPLACE = "$1-$2-$3 00:00:00.000";

    /**
     * 倒序的日期格式
     */

    public static final String DATE_FORMAT_REVERT_REGEX = "^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{4,})$";

    /**
     * 有符号正常日期格式替换
     */

    public static final String DATE_FORMAT_REVERT_REPLACE = "$3-$2-$1 00:00:00.000";

    /**
     * 正常时间格式
     */

    public static final String DATETIME_HOUR_FORMAT_REGEX = "^(\\d{4,})[/-](\\d{1,2})[/-](\\d{1,2}).(\\d{1,2}):(\\d{1,2})$";

    /**
     * 正常时间格式替换
     */

    public static final String DATETIME_HOUR_FORMAT_REPLACE = "$1-$2-$3 $4:$5:00.000";

    /**
     * 正常时间格式
     */
    public static final String DATETIME_FORMAT_REGEX = "^(\\d{4,})[/-](\\d{1,2})[/-](\\d{1,2}).(\\d{1,2}):(\\d{1,2}):(\\d{1,2})$";

    /**
     * 正常时间格式替换
     */
    public static final String DATETIME_FORMAT_REPLACE = "$1-$2-$3 $4:$5:$6.000";

    /**
     * ISO8601时间格式
     */
    public static final String DATETIME_ISO8601_FORMAT_REGEX = "^(\\d{4,})[/-](\\d{1,2})[/-](\\d{1,2})T(\\d{1,2}):(\\d{1,2}):(\\d{1,2})\\.(\\d{1,3})(Z+|\\+.*)$";

    /**
     * ISO8601时间格式替换
     */
    public static final String DATETIME_ISO8601_FORMAT_REPLACE = "$1-$2-$3 $4:$5:$6.$7";

    /**
     * 正常时间格式
     */
    public static final String ZERO_FILL_REGEX = "^(\\d{4,})[/-](\\d{1,2})[/-](\\d{1,2}).(\\d{1,2}):(\\d{1,2}):(\\d{1,2})\\.?(\\d{1,3})?$";


    /**
     * 缓存的自动识别的格式正则表达式
     */
    private static final List<DateReplace> autoDateCache = new ArrayList<>();

    static {
        registerAutoFormat(DATETIME_ISO8601_FORMAT_REGEX, DATETIME_ISO8601_FORMAT_REPLACE);
        registerAutoFormat(DATETIME_FORMAT_REGEX, DATETIME_FORMAT_REPLACE);
        registerAutoFormat(DATETIME_HOUR_FORMAT_REGEX, DATETIME_HOUR_FORMAT_REPLACE);
        registerAutoFormat(DATE_FORMAT_REVERT_REGEX, DATE_FORMAT_REVERT_REPLACE);
        registerAutoFormat(DATE_FORMAT_COMMON_REGEX, DATE_FORMAT_COMMON_REPLACE);
        registerAutoFormat(DATE_FORMAT_NO_SPLIT_REGEX, DATE_FORMAT_COMMON_REPLACE);
    }

    /**
     * 时间格式字符串
     */

    private static class DateReplace {
        /**
         * 正则表达式
         */
        public String regex;

        /**
         * 替换表达式
         */
        public String replace;
    }


    /**
     * 注册正则表达式，将时间转换为正确格式的正则表达式，后注册的会优先执行 。
     *
     * @param regex   正则表达式
     * @param replace 替换表达式
     */
    public static void registerAutoFormat(String regex, String replace) {
        DateReplace item = new DateReplace();
        item.regex = regex;
        item.replace = replace;
        autoDateCache.add(item);
    }

    /**
     * 根据时间字符串自动识别时间
     *
     * @param date 时间字符串
     * @return 时间
     */
    public static LocalDateTime autoParse(String date) {
        if (date == null) {
            return null;
        }

        String dateTo = null;
        for (DateReplace dateReplace : autoDateCache) {
            dateTo = date.replaceAll(dateReplace.regex, dateReplace.replace);
            if (!dateTo.equals(date)) {
                break;
            }
        }

        return parseDateTime(zeroFill(dateTo), FULL_PATTERN_DATETIME);
    }


    /**
     * 对于不足位数的日期或时间补零，如1700-1-1 4:2:3.23 -> 1700-01-01 04:02:03.023
     *
     * @param date 日期字符串
     * @return 补零后的日期字符串
     */
    public static String zeroFill(String date) {
        Pattern pattern = Pattern.compile(ZERO_FILL_REGEX);
        Matcher matcher = pattern.matcher(date);
        if (matcher.matches()) {
            StringBuilder sb = new StringBuilder();
            sb.append(String.format("%04d", Integer.valueOf(matcher.group(1))));
            sb.append(StringPool.DASH);
            sb.append(String.format("%02d", Integer.valueOf(matcher.group(2))));
            sb.append(StringPool.DASH);
            sb.append(String.format("%02d", Integer.valueOf(matcher.group(3))));
            sb.append(StringPool.SPACE);
            sb.append(String.format("%02d", Integer.valueOf(matcher.group(4))));
            sb.append(StringPool.COLON);
            sb.append(String.format("%02d", Integer.valueOf(matcher.group(5))));
            sb.append(StringPool.COLON);
            sb.append(String.format("%02d", Integer.valueOf(matcher.group(6))));
            if (null != matcher.group(7)) {
                sb.append(StringPool.DOT);
                sb.append(String.format("%03d", Integer.valueOf(matcher.group(7))));
            }
            return sb.toString();
        }
        return date;
    }

    /**
     * 转换为时间戳，单位：秒
     * @param dateTime 时间
     * @return 秒时间戳
     */
    public static long toTimestamp(LocalDateTime dateTime) {
        return dateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
    }
}
